home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Applications / NIH Image 1.62b11 / src / Dicom.p < prev    next >
Text File  |  1996-11-22  |  22KB  |  818 lines

  1. {
  2. Dicom.p
  3.   by: Jim Nash, Synergistic Research Systems (jim.nash@his.com)
  4.   Reads and decodes the DICOM header so that NIH Image can
  5.   import DICOM images. DICOM (Digital Imaging and Communications
  6.   in Medicine) is a format popular in the medical imaging
  7.   community. This code is in the public domain.
  8. }
  9.  
  10.  
  11. unit DICOM;
  12.  
  13. interface
  14.  
  15.     uses
  16.         Types, Memory, QuickDraw, Packages, Menus, Events, Fonts, Scrap, ToolUtils, Resources, 
  17.         Errors, Palettes, Printing, StandardFile, Folders, TextUtils, Files,
  18.         globals, Utilities, Text, Graphics, Utilities, file2;
  19.  
  20.  
  21.     procedure ImportDICOMImages (fname: Str255; RefNum: integer; ImportAll: boolean; {}
  22.                                     function ImportFile (fname: str255; vnum: integer): boolean);
  23.  
  24. implementation
  25.  
  26.     const
  27.         dDicomName = 'DICOM dictionary';
  28.         maxElements = 1000;
  29.         elemNameLength = 50;
  30.     type
  31.         DataKind = (kUnknown, kString, kInteger, kLongint, kReal, kUInteger, kULongint);
  32.  
  33.         DataElement = record
  34.                 group, element: integer;
  35.                 code: packed array[1..2] of char;
  36.                 list: boolean;
  37.                 name: string[elemNameLength];
  38.             end;
  39.         ElemArray = array[1..maxElements] of DataElement;
  40.         ElemArrayPtr = ^ElemArray;
  41.  
  42.         DataDictionary = record
  43.                 number: integer;
  44.                 elem: ElemArrayPtr;
  45.             end;
  46.         DataDictionaryPtr = ^DataDictionary;
  47.     var
  48.         dictionary: DataDictionaryPtr;
  49.         loaded: boolean;
  50.         mySliceSpacing: real;
  51.  
  52.  
  53. { **************  Utility routines ***************** }
  54.  
  55.     procedure StringToBase (s: Str255; base: integer; var value: longint);
  56. {converts a string in some base to longint.  Typically}
  57. {base = 2,8,10,16 to represent binary, octal, decimal and hexadecimal}
  58.         var
  59.             ch: char;
  60.             good: boolean;
  61.             len, digit: integer;
  62.             i: longint;
  63.     begin
  64.         i := 1;
  65.         value := 0;
  66.         len := length(s);
  67.         while (i <= len) do begin
  68.             good := true;
  69.             ch := s[i];
  70.             if ch in ['A'..'Z'] then
  71.                 digit := ord(ch) - ord('A') + 10
  72.             else if ch in ['a'..'z'] then
  73.                 digit := ord(ch) - ord('a') + 10
  74.             else if ch in ['0'..'9'] then
  75.                 digit := ord(ch) - ord('0')
  76.             else
  77.                 good := false;
  78.             if good then
  79.                 value := value * base + digit;
  80.             i := i + 1;
  81.         end;
  82.     end;
  83.  
  84.  
  85.     procedure BaseToString (value: longint; base: integer; var s: Str255);
  86. {converts a long integer to a string in any base.  Typically}
  87. {base = 2,8,10,16 to represent binary, octal, decimal and hexadecimal.}
  88. {Ignores the sign bit unless base=10.}
  89.         var
  90.             sign, decimal: boolean;
  91.             digit: integer;
  92.             ch: char;
  93.     begin
  94.         decimal := (base = 10);
  95.         s := '';
  96.         sign := (value < 0);
  97.         if decimal then
  98.             value := abs(value)
  99.         else
  100.             value := BAND(value, $7FFFFFFF);
  101.         if value = 0 then
  102.             s := '0'
  103.         else
  104.             while (value <> 0) do begin
  105.                 digit := value mod base;
  106.                 value := value div base;
  107.                 if (digit >= 0) and (digit <= 9) then
  108.                     ch := chr(digit + ord('0'))
  109.                 else
  110.                     ch := chr(digit - 10 + ord('A'));
  111.                 s := concat(ch, s);
  112.             end;
  113.         if sign then
  114.             if decimal then
  115.                 s := concat('-', s)
  116.             else if s[1] < '2' then
  117.                 s[1] := chr(ord(s[1]) + 8)
  118.             else
  119.                 s[1] := chr(ord(s[1]) - ord('2') + ord('A'));
  120.     end;
  121.  
  122.  
  123.     function htos (i: longint): Str255;
  124. {A convenience function to replace BaseToString (hexadecimal) }
  125.         var
  126.             s: Str255;
  127.     begin
  128.         BaseToString(i, 16, s);
  129.         htos := s;
  130.     end;
  131.  
  132.  
  133.     function itos (i: longint): Str255;
  134. {A convenience function to replace NumToString}
  135.         var
  136.             s: Str255;
  137.     begin
  138.         NumToString(i, s);
  139.         itos := s;
  140.     end;
  141.  
  142.  
  143. { **************  DICOM routines ***************** }
  144.  
  145.  
  146.     procedure InitDICOM;
  147.         var
  148.             err: integer;
  149.     begin
  150.         dictionary := nil;
  151.         loaded := false;
  152.         DicomInitialized:=true;
  153.     end;
  154.  
  155.  
  156.  
  157.     function FindDicomElement (group, element: integer): integer;
  158.         var
  159.             i, index: integer;
  160.     begin
  161.         index := 0;
  162.         if loaded and (dictionary <> nil) then
  163.             with dictionary^ do
  164.                 if (elem <> nil) then begin
  165.                     i := 1;
  166.                     while (group > elem^[i].group) and (i < number) do
  167.                         i := i + 1;
  168.                     if (i <= number) then
  169.                         while (element > elem^[i].element) and (group = elem^[i].group) and (i < number) do
  170.                             i := i + 1;
  171.                     if (i <= number) then
  172.                         if (element = elem^[i].element) and (group = elem^[i].group) then
  173.                             index := i;
  174.                 end;
  175.         FindDicomElement := index;
  176.     end;
  177.  
  178.  
  179.     procedure InitUserChoice;
  180. {selected elements to list in text window, short form.}
  181. {This a minimum set.  If a user wants more information, they do a full dump.}
  182.  
  183.         procedure Select (group, element: integer);
  184.             var
  185.                 index: integer;
  186.         begin
  187.             with dictionary^ do begin
  188.                 index := FindDicomElement(group, element);
  189.                 if index > 0 then
  190.                     elem^[index].list := true;
  191.             end;
  192.         end;
  193.  
  194.     begin
  195.         with dictionary^ do begin
  196.             Select($8, $20);
  197.             Select($8, $30);
  198.             Select($8, $60);
  199.             Select($8, $1030);
  200.             Select($8, $103E);
  201.             Select($8, $1070);
  202.  
  203.             Select($10, $10);
  204.             Select($10, $20);
  205.             Select($10, $21B0);
  206.  
  207.             Select($18, $10);
  208.             Select($18, $50);
  209.             Select($18, $88);
  210.  
  211.             Select($20, $10);
  212.             Select($20, $11);
  213.             Select($20, $12);
  214.             Select($20, $13);
  215.  
  216.             Select($28, $10);
  217.             Select($28, $11);
  218.             Select($28, $30);
  219.             Select($28, $100);
  220.         end;
  221.     end;
  222.  
  223.  
  224.     procedure LoadDataDictionary;
  225.         type
  226.             CharBuf = packed array[0..100000] of char;
  227.             CharBufPtr = ^CharBuf;
  228.         var
  229.             err, refnum, len, i1, i2, n: integer;
  230.             index1, index2, logEOF, count, num, theSize: longint;
  231.             f: text;
  232.             sp: StringPtr;
  233.             str: Str255;
  234.             s1: Str255;
  235.             buf: CharBufPtr;
  236.  
  237.     begin
  238.         if dictionary = nil then begin
  239.             dictionary := DataDictionaryPtr(NewPtr(sizeof(DataDictionary)));
  240.             if dictionary <> nil then
  241.                 dictionary^.elem := ElemArrayPtr(NewPtr(sizeof(ElemArray)));
  242.             loaded := false;
  243.         end;
  244.         if (not loaded) and (dictionary <> nil) then
  245.             with dictionary^ do begin
  246.                 err := HSetVol(nil, StartupSpec.vRefNum, StartupSpec.parID);
  247.                 err := FSOpen(dDicomName, 0, refnum);     {check that file is present}
  248.                 if (err = 0) and (elem <> nil) then begin
  249.                     err := GetEOF(refnum, logEOF);
  250.                     buf := CharBufPtr(NewPtr(logEOF + 10));
  251.                     if (buf <> nil) then begin
  252.                         loaded := true;
  253.                         number := 0;
  254.                         count := logEOF;
  255.                         err := FSRead(refnum, count, ptr(buf));
  256.                         err := FSClose(refnum);
  257.                         index1 := 0;
  258.                         repeat
  259.                             index2 := index1;
  260.                             str := '';
  261.                             while (buf^[index2] <> cr) and (index2 < logEOF) and (length(str) < 255) do begin
  262.                                 str := concat(str, buf^[index2]);
  263.                                 index2 := index2 + 1;
  264.                             end;
  265.                             index1 := index2 + 1;
  266.                             len := length(str);
  267.                             if len > 0 then
  268.                                 if str[1] = '{' then begin
  269.                                     number := number + 1;
  270.                                     if (number mod 10) = 0 then
  271.                                         ShowAnimatedWatch;
  272.                                     with elem^[number] do begin
  273.                                         list := false;
  274.  
  275.                                         i1 := pos('x', str);
  276.                                         s1 := copy(str, i1 + 1, 4);
  277.                                         StringToBase(s1, 16, num);
  278.                                         group := num;
  279.                                         str := copy(str, i1 + 6, length(str)-(i1 + 6));
  280.  
  281.                                         i1 := pos('x', str);
  282.                                         s1 := copy(str, i1 + 1, 4);
  283.                                         StringToBase(s1, 16, num);
  284.                                         element := num;
  285.                                         str := copy(str, i1 + 6, length(str)-(i1 + 6));
  286.  
  287.                                         i1 := pos('''', str);
  288.                                         if length(str) >= (i1 + 2) then begin
  289.                                             code[1] := str[i1 + 1];
  290.                                             code[2] := str[i1 + 2];
  291.                                             str := copy(str, i1 + 5, length(str)-(i1 + 5));
  292.                                         end
  293.                                         else
  294.                                             str := '';
  295.  
  296.                                         i1 := pos('"', str);
  297.                                         if i1 > 0 then
  298.                                             str[i1] := ' ';
  299.                                         i2 := pos('"', str);
  300.                                         if i2 = 0 then
  301.                                             number := number - 1
  302.                                         else begin
  303.                                             n := i2 - i1 - 1;
  304.                                             if n > elemNameLength then
  305.                                                 n := elemNameLength;
  306.                                             name := copy(str, i1 + 1, n);
  307.                                         end;
  308.                                     end;
  309.                                 end;
  310.                         until (index1 >= logEOF);
  311.                     end;
  312.                     DisposePtr(ptr(buf));
  313.                 end;
  314.                 InitUserChoice;
  315.             end;
  316.     end;
  317. {$R+}
  318.  
  319.  
  320.     function GetDataKind (index: integer): DataKind;
  321.         var
  322.             kind: DataKind;
  323.     begin
  324.         kind := kUnknown;
  325.         if (dictionary <> nil) and (index > 0) then
  326.             with dictionary^.elem^[index] do begin
  327.                 if (code = 'AE') or (code = 'AS') or (code = 'CS') or (code = 'DA') or (code = 'DS') then
  328.                     kind := kString
  329.                 else if (code = 'DT') or (code = 'IS') or (code = 'LO') or (code = 'LT') or (code = 'PN') then
  330.                     kind := kString
  331.                 else if (code = 'SH') or (code = 'ST') or (code = 'TM') or (code = 'UI') then
  332.                     kind := kString
  333.                 else if (code = 'SS') then
  334.                     kind := kInteger
  335.                 else if (code = 'SL') then
  336.                     kind := kLongint
  337.                 else if (code = 'US') then
  338.                     kind := kUInteger
  339.                 else if (code = 'UL') then
  340.                     kind := kULongint;
  341.             end;
  342.         GetDataKind := kind;
  343.     end;
  344.  
  345.     procedure ImportDICOMImages (fname: Str255; RefNum: integer; ImportAll: boolean; {}
  346.                                     function ImportFile (fname: str255; vnum: integer): boolean);
  347.         var
  348.             enable_text, enable_open_text, first_image, sw, listAll, UseFixedScale: boolean;
  349.             ImageNumber:integer;
  350.             myIntercept, myScale: extended;
  351.  
  352.         function GetDICOMParams (fname: Str255; vNum: integer): integer;
  353.             const
  354.                 id_offset = 128;            {location of "DICM"}
  355.                 firstDicomElement = 132;    {first element}
  356.                 maxbuf = 20000;
  357.             type
  358.                 name4 = packed array[1..4] of char;
  359.                 name4ptr = ^name4;
  360.             var
  361.                 open_sw, done, window_sw: boolean;
  362.                 f, err, index, len: integer;
  363.                 groupWord, elementWord, lastGroup, FirstElement: integer;
  364.                 height, width, bits_alloc, bits_stored, high_bit, representation, offset, bitsAllocated: integer;
  365.                 seriesMin, seriesMax: integer;           {intensity range}
  366.                 scale, aspect, units: Str255;       {spatial}
  367.                 s, imgNumString, sliceSpacingStg, rescaleInterceptStg, rescaleSlopeStg: Str255;
  368.                 buflen, elementLength, groupLength: longint;
  369.                 buf: packed array[0..maxbuf] of byte;
  370.                 kind: DataKind;
  371.                 dictionaryIndex: integer;
  372.                 xStr,yStr, vr:str255;
  373.  
  374.             procedure MyWriteElement (str: Str255);
  375.                 const
  376.                     spaces = '                                                                                               ';
  377.                     padWidth = 4;
  378.                     nameWidth = 32;
  379.                 var
  380.                     s1, s2: str255;
  381.  
  382.                 function pad (s: Str255): Str255;
  383.                     const
  384.                         width = 4;
  385.                 begin
  386.                     while length(s) < width do
  387.                         s := concat(' ', s);
  388.                     pad := s;
  389.                 end;
  390.  
  391.             begin
  392.                 with dictionary^.elem^[dictionaryIndex] do begin
  393.                     s2 := name;
  394.                     if listAll then begin
  395.                         s1 := concat('(', pad(htos(groupWord)), ',', pad(htos(elementWord)), ')  (', pad(itos(elementLength)), ')');
  396.                         s2 := copy(concat(name, spaces), 1, nameWidth);
  397.                         s2 := concat(s1, '  ', code[1], code[2], '  ', s2);
  398.                     end;
  399.                     str := concat(s2, ':  ', str);
  400.                     if enable_text then begin
  401.                         if groupWord <> lastGroup then
  402.                             InsertText('', true);
  403.                         lastGroup := groupWord;
  404.                         InsertText(str, true);
  405.                     end;
  406.                 end;
  407.             end;
  408.  
  409.             function GetInteger (index: integer): integer;
  410.                 var
  411.                     i: integer;
  412.             begin
  413.                 i := buf[index] + $100 * buf[index + 1];
  414.                 GetInteger := i;
  415.             end;
  416.  
  417.             function GetLongint (index: integer): longint;
  418.                 var
  419.                     i: longint;
  420.             begin
  421.                 i := Ord4(buf[index]) + $100 * (buf[index + 1] + $100 * (buf[index + 2] + $100 * buf[index + 3]));
  422.                 GetLongint := i;
  423.             end;
  424.  
  425.             function GetLength (i: integer): longint;
  426.             begin
  427.                 vr[1] := chr(buf[i]);
  428.                 vr[2] := chr(buf[i + 1]);
  429.                 if vr[2] < 'A' then {implecit vr with 32-bit length}
  430.                     GetLength := Ord4(buf[i]) + $100 * (buf[i+1] + $100 * (buf[i+2] + $100 * buf[i+3]))
  431.                 else if (vr = 'OB') or (vr = 'OW') or (vr = 'SQ') then begin  {explicit VR with 32-bit length}
  432.                     i := i + 4;  {skip 2 byte string and 2 reserved bytes}
  433.                     index := index + 4;
  434.                     GetLength := Ord4(buf[i]) + $100 * (buf[i+1] + $100 * (buf[i+2] + $100 * buf[i+3]))
  435.                 end else  {explicit VR with 16-bit length}
  436.                     GetLength := Ord4(buf[i+2]) + $100 * (buf[i+3]);
  437.             end;
  438.  
  439.             function GetUInteger (index: integer): longint;
  440.                 var
  441.                     i: integer;
  442.             begin
  443.                 i := buf[index] + $100 * buf[index + 1];
  444.                 GetUInteger := BAND(i, $FFFF);
  445.             end;
  446.  
  447.             function GetULongint (index: integer): longint;
  448.             {does not correctly report numbers > $7FFFFFFF}
  449.             begin
  450.                 GetULongint := GetLongint(index);
  451.             end;
  452.  
  453.             function GetString (index: integer): Str255;
  454.                 var
  455.                     i: integer;
  456.                     s: Str255;
  457.             begin
  458.                 s := '';
  459.                 for i := 0 to elementLength - 1 do
  460.                     s := concat(s, chr(buf[index + i]));
  461.                 GetString := s;
  462.             end;
  463.  
  464.         
  465.             function htos2(i: LongInt): str255;
  466.             {Converts an integer to hex using a fixed field width of 6}
  467.             var
  468.                 s: str255;
  469.             begin
  470.                 s := htos(i);
  471.                 while length(s) < 6 do
  472.                     s := concat(' ', s);
  473.                 htos2 := s;
  474.             end;
  475.  
  476.  
  477.             procedure DoByteSwap (var i: LongInt);
  478.                 var
  479.                     a: ostype;
  480.                     c: char;
  481.             begin
  482.                 a := ostype(i);
  483.                 c := a[1];
  484.                 a[1] := a[2];
  485.                 a[2] := c;
  486.                 c := a[3];
  487.                 a[3] := a[4];
  488.                 a[4] := c;
  489.                 i := LongInt(a)
  490.             end;
  491.             
  492.             
  493.     procedure Swap(var i: LongInt);
  494.         var
  495.             a: ostype;
  496.             c: char;
  497.     begin
  498.         a := ostype(i);
  499.         a[1] := a[3];
  500.         a[2] := a[4];
  501.         a[3] := chr(0);
  502.         a[4] := chr(0);
  503.         i := LongInt(a)
  504.     end;
  505.  
  506.  
  507.             procedure GetNextElement;
  508.                 var
  509.                     i: longint;
  510.                     str: Str255;
  511.             begin
  512.                 if index = 0 then
  513.                     index := firstElement
  514.                 else
  515.                     index := index + elementLength;
  516.                 if (index < 0) or (index >= buflen) then
  517.                     exit(GetNextElement);
  518.                 groupWord := GetInteger(index);
  519.                 elementWord := GetInteger(index + 2);
  520.                 elementLength := GetLength(index + 4);
  521.                 if elementLength = 13 then {hack needed to read some GE files}
  522.                     elementLength := 10;
  523.                 if ControlKeyDown then
  524.                     InsertText(stringOf(index:6, htos2(groupWord), htos2(elementWord), htos2(elementLength)), true);
  525.                 index := index + 8;
  526.                 dictionaryIndex := FindDicomElement(groupWord, elementWord);
  527.                 if dictionaryIndex > 0 then
  528.                     with dictionary^ do
  529.                         if elem^[dictionaryIndex].list or listAll then
  530.                             with elem^[dictionaryIndex] do begin
  531.                                 kind := GetDataKind(dictionaryIndex);
  532.                                 case kind of
  533.                                     kString: 
  534.                                         str := GetString(index);
  535.                                     kInteger: 
  536.                                         str := itos(GetInteger(index));
  537.                                     kLongint: 
  538.                                         str := itos(GetLongint(index));
  539.                                     kUInteger: 
  540.                                         str := itos(GetLongint(index));
  541.                                     kULongint: 
  542.                                         str := itos(GetULongint(index));
  543.                                     otherwise
  544.                                         str := 'unknown format';
  545.                                 end;
  546.                                 MyWriteElement(str);
  547.                             end;
  548.             end;
  549.  
  550.             function IsElement (group, element: integer): boolean;
  551.             begin
  552.                 IsElement := (group = groupWord) and (element = elementWord);
  553.             end;
  554.  
  555.             procedure TestError (err1: integer; str: Str255);
  556.                 var
  557.                     str1: Str255;
  558.             begin
  559.                 err := err1;
  560.                 if err1 <> 0 then begin
  561.                     if err <> 1 then
  562.                         str := concat(str, ' - error ', itos(err));
  563.                     PutMessage(str);
  564.                     if open_sw then
  565.                         err1 := fsclose(f);
  566.                     GetDICOMParams := err;
  567.                     exit(GetDICOMParams);
  568.                 end;
  569.             end;
  570.  
  571.             procedure OpenDicomTextWindow;
  572.                 var
  573.                     width, height: integer;
  574.             begin
  575.                 if listAll then begin
  576.                     width := 500;
  577.                     height := 400;
  578.                 end
  579.                 else begin
  580.                     width := 350;
  581.                     height := 300;
  582.                 end;
  583.                 if enable_open_text then
  584.                     window_sw := MakeNewTextWindow(concat(fname, ' header'), width, height);
  585.                 CurrentFontID := monaco;
  586.                 CurrentSize := 9;
  587.                 ChangeFontOrSize;
  588.                 enable_open_text := false;
  589.                 if enable_text then begin
  590.                     if loaded then
  591.                         InsertText('Selected fields from the DICOM file header', true)
  592.                     else begin
  593.                         InsertText(concat('Can''t find file: ', dDicomName, '.'), true);
  594.                         InsertText('', true);
  595.                         InsertText('This file is required to decode the DICOM header. It is available from: ftp://zippy.nimh.nih.gov/pub/nih-image/documents/dicom-dict.hqx. It must be located in the same folder as NIH Image or in the System folder.', true)
  596.                     end;
  597.                     InsertText('', true);
  598.                 end;
  599.             end;
  600.  
  601.         begin
  602.             vr :='--';
  603.             err := 0;
  604.             buflen := maxbuf + 1;
  605.             open_sw := false;
  606.             TestError(fsopen(fname, vNum, f), 'Open');
  607.             open_sw := true;
  608.             TestError(FSRead(f, buflen, @buf), 'Read');
  609.             TestError(fsclose(f), 'Close');
  610.             if name4ptr(longint(@buf) + id_offset)^ = 'DICM' then
  611.                 FirstElement:=FirstDicomElement
  612.             else if name4ptr(longint(@buf))^ = 'DICM' then
  613.                 FirstElement:=4
  614.             else
  615.                 FirstElement:=0; {TestError(1, 'This is not a DICOM file.');}
  616.             OpenDicomTextWindow;
  617.             sliceSpacingStg := '1.0';
  618.             rescaleInterceptStg := '0.0';
  619.             rescaleSlopeStg := '1.0';
  620.             imgNumString := '';
  621.             height := -1;
  622.             width := -1;
  623.             offset := -1;
  624.             index := 0;
  625.             lastGroup := $8;
  626.             done := false;
  627.             scale := '0.0';
  628.             aspect := '1.0';
  629.             representation := 0;
  630.             bitsAllocated := 16;
  631.             seriesMin := 0;
  632.             seriesMax := 0;
  633.             units := '';
  634.             repeat
  635.                 GetNextElement;
  636.                 if (index < 0) or (index >= buflen) then
  637.                     leave;
  638.                 if (elementWord = 0) and (elementLength = 0) then
  639.                     leave;
  640.  
  641.                 if IsElement($18, $88) then
  642.                     sliceSpacingStg := GetString(index)
  643.                 else if IsElement($20, $13) then
  644.                     imgNumString := GetString(index)
  645.                 else if IsElement($28, $10) then
  646.                     height := GetInteger(index)
  647.                 else if IsElement($28, $11) then
  648.                     width := GetInteger(index)
  649.                 else if IsElement($28, $30) then
  650.                     scale := GetString(index)
  651.                 else if IsElement($28, $34) then
  652.                     aspect := GetString(index)
  653.                 else if IsElement($28, $100) then
  654.                     bitsAllocated := GetInteger(index)
  655.                 else if IsElement($28, $103) then
  656.                     representation := GetInteger(index)
  657.                 else if IsElement($28, $108) then
  658.                     seriesMin := GetInteger(index)
  659.                 else if IsElement($28, $109) then
  660.                     seriesMax := GetInteger(index)
  661.                 else if IsElement($28, $1052) then
  662.                     rescaleInterceptStg := GetString(index)
  663.                 else if IsElement($28, $1053) then
  664.                     rescaleSlopeStg := GetString(index)
  665.                 else if IsElement($7FE0, $10) then begin
  666.                     offset := index;
  667.                     done := true;
  668.                 end;
  669.                 if CommandPeriod then
  670.                     listAll := false;
  671.             until done;
  672.             if (width = -1) or (height = -1) or (offset = -1) then begin
  673.                 InsertText('', true);
  674.                 InsertText('Unable to decode DICOM header.', true);
  675.                 GetDICOMParams := -1;
  676.                 exit(GetDICOMParams)
  677.             end;
  678.  
  679.             {Image dimension information}
  680.             ImportCustomWidth := width;
  681.             ImportCustomHeight := height;
  682.             ImportCustomOffset := offset;
  683.             ImportSwapBytes := true; {(representation = 1);}
  684.  
  685.             {Intensity information}
  686.             if bitsAllocated = 8 then begin
  687.                 ImportCustomDepth := EightBits;
  688.                 if ImageNumber=1 then
  689.                     InsertText('', true);
  690.                 GetDICOMParams := err;
  691.                 exit(GetDICOMParams);
  692.             end else
  693.                 ImportCustomDepth := SixteenBitsSigned;
  694.             if not ((seriesMin = 0) and (seriesMax = 0)) then begin
  695.                 ImportAutoScale:=false;
  696.                 ImportMin:=seriesMin;
  697.                 ImportMax:=seriesMax;
  698.             end else if (ImageNumber>1) and UseFixedScale then begin
  699.                 ImportAutoScale:=false;
  700.                 ImportMin:=info^.CurrentMin;
  701.                 ImportMax:=info^.CurrentMax;
  702.             end else
  703.                 ImportAutoScale:=true;;
  704.  
  705.             {convert from scaled units to independent units}
  706.             myIntercept := StringToReal(rescaleInterceptStg);
  707.             myScale := StringToReal(rescaleSlopeStg);
  708.  
  709.             {Spatial scale information}
  710.             with Info^ do begin
  711.                 PixelAspectRatio := StringToReal(aspect);
  712.                 xScale := 1;
  713.                 yScale := 1;
  714.                 zScale := 1.0 / StringToReal(sliceSpacingStg);
  715.  
  716.                 xUnit := '';
  717.                 SpatiallyCalibrated := false;
  718.                 if scale <> '' then begin
  719.                     xStr:=copy(scale, pos('\', scale) + 1, length(scale) - pos('\', scale));
  720.                     xScale := StringToReal(xStr);
  721.                     yStr:=copy(scale, 1, pos('\', scale) - 1);
  722.                     yScale := StringToReal(yStr);
  723.                     xUnit := 'mm';
  724.                     SpatiallyCalibrated := (xScale <> 0.0) and (yScale <> 0.0);
  725.                     if SpatiallyCalibrated then begin
  726.                         xScale := 1.0 / xScale;
  727.                         yScale := 1.0 / yScale;
  728.                     end;
  729.                 end;
  730.             end; {with}
  731.             if ImageNumber=1 then
  732.                 InsertText('', true);
  733.             GetDICOMParams := err;
  734.         end;
  735.  
  736.  
  737.         procedure UpdateCoefficients;
  738.         {Scale coefficients given Dicom Rescale Intercept and Rescale Slope}
  739.         begin
  740.             with info^ do begin
  741.                 info^.Coefficient[1] := myIntercept + myScale * info^.Coefficient[1];
  742.                 info^.Coefficient[2] := myScale * info^.Coefficient[2];
  743.                 fit := StraightLine;
  744.                 GenerateValues;
  745.             end;
  746.         end;
  747.         
  748.  
  749.         procedure ImportAllDicomFiles (RefNum: integer);
  750.             var
  751.                 OpenedOK: boolean;
  752.                 index: integer;
  753.                 name: Str255;
  754.                 ftype: OSType;
  755.                 err: OSErr;
  756.                 PB: HParamBlockRec;
  757.         begin
  758.             index := 0;
  759.             while true do begin
  760.                 index := index + 1;
  761.                 with PB do begin
  762.                     ioCompletion := nil;
  763.                     ioNamePtr := @name;
  764.                     ioVRefNum := RefNum;
  765.                     ioVersNum := 0;
  766.                     ioFDirIndex := index;
  767.                     err := PBGetFInfoSync(@PB);
  768.                     if err = fnfErr then
  769.                         exit(ImportAllDicomFiles);
  770.                     ftype := ioFlFndrInfo.fdType;
  771.                 end;
  772.  
  773.                 if GetDICOMParams(name, RefNum) <> 0 then
  774.                     exit(ImportAllDicomFiles);
  775.                 WhatToImport := ImportCustom;
  776.                 if not ImportFile(name, RefNum) then
  777.                     exit(ImportAllDicomFiles);
  778.                 WhatToImport := ImportDicom;
  779.                 if (myIntercept <> 0.0) or (myScale <> 1.0) then
  780.                     UpdateCoefficients;
  781.                 with info^ do InsertText(StringOf(ImageNumber:3, ': "', title, '", min=', CurrentMin:1, ', max=', CurrentMax:1), true);
  782.                 ImageNumber:=ImageNumber+1;
  783.                 enable_text := false;        {text saved for first image only}
  784.                 first_image := false;
  785.                 if CommandPeriod then begin
  786.                     beep;
  787.                     exit(ImportAllDicomFiles);
  788.                 end;
  789.             end;
  790.         end;
  791.  
  792.     begin        {ImportDICOMImages}
  793.         if not DicomInitialized then
  794.             InitDICOM;
  795.         listAll := OptionKeyDown or OptionKeyWasDown;
  796.         enable_open_text := true;
  797.         enable_text := true;
  798.         first_image := true;
  799.         ImageNumber:=1;
  800.         UseFixedScale:=ShiftKeyDown;
  801.         LoadDataDictionary;
  802.         ImportingDicom := true;
  803.         if ImportAll then
  804.             ImportAllDicomFiles(RefNum)
  805.         else begin
  806.             if GetDICOMParams(fname, RefNum) <> 0 then
  807.                 exit(ImportDICOMImages);
  808.             WhatToImport := ImportCustom;
  809.             if ImportFile(fname, RefNum) then
  810.                 if (myIntercept <> 0.0) or (myScale <> 1.0) then
  811.                     UpdateCoefficients;
  812.             WhatToImport := ImportDicom;
  813.             with info^ do InsertText(StringOf('file "', title, '", min=', CurrentMin:1, ', max=', CurrentMax:1), true);
  814.         end;
  815.         ImportingDicom := false;
  816.     end;
  817.  
  818. end.